/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2008，所有权利保留。
 * 
 * 项目名：	rumba-web
 * 文件名：	BProcDispatcher.java
 * 模块说明：	
 * 修改历史：
 * Jul 27, 2008 - lxm - 创建。
 */
package com.hd123.rumba.gwt.util.client.batchprocess;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.user.client.Timer;

/**
 * 批处理调度器。设计提供当程序中需要对于一系列操作进行批处理，提供必要的封装以简化这类程序的编写。
 * <p>
 * 
 * 作为调用者更常见的一种调用方式，是通过指定一组数据{@link #setRecords()}，每次调度都是针对这组数据中的某一个数据记录。 调度器将会自动进行数据下标控制。
 * 
 * @author lxm
 * 
 */
public class BProcDispatcher {

  /**
   * 构造函数。
   * 
   * @param listener
   *          指定侦听器对象。
   * 
   */
  public BProcDispatcher(BProcDispatcherListener listener) {
    super();
    this.listener = listener;
  }

  private IProgressPanel progressPanel = null;
  private BProcDispatcherListener listener = null;

  private Object[] records;
  private int recordIndex = -1;
  private Map env = new HashMap();
  private boolean running = false;

  /** 将被迭代的数据记录。 */
  public Object[] getRecords() {
    return records;
  }

  public void setRecords(Object[] records) {
    this.records = records;
  }

  /** 进度显示面板（对话框） */
  public IProgressPanel getProgressPanel() {
    return progressPanel;
  }

  public void setProgressPanel(IProgressPanel progressPanel) {
    this.progressPanel = progressPanel;
  }

  /**
   * 设置批处理调度侦听器对象。
   * 
   * @param listener
   */
  public void setListener(BProcDispatcherListener listener) {
    this.listener = listener;
  }

  /**
   * 取得环境。是一个Map，调用者可以利用其传递批处理所需要的参数，也可以在各原子处理之间传递数据。
   * 
   * @return
   */
  public Map getEnv() {
    return env;
  }

  /**
   * 开始指定批处理。
   * 
   * @throws ClientBizException
   */
  public void start() {
    //    ObjectUtil.assertion(listener != null, "必须指定所对应的侦听器对象。");

    if (records != null) {
      recordIndex = 0;
      if (progressPanel != null) {
        progressPanel.setMin(0);
        progressPanel.setMax(records.length);
        progressPanel.setPos(0);
      }
    }

    if (listener != null)
      listener.onStart(this);

    if (progressPanel != null)
      progressPanel.show();

    running = true;
    dispatch();
  }

  /**
   * 通知调度器执行下一步。对于指定了records的情况下，若已经到最后一个则等价于调用{@link #over()}。
   */
  public void next() {
    if (!running)
      return;
    if (records != null) {
      recordIndex++;

      if (progressPanel != null)
        progressPanel.stepBy(1);

      if (recordIndex >= records.length) {
        complete();
        return;
      }
    }

    if (isInterrupted())
      interrupt();
    else
      dispatch();
  }

  /**
   * 通知调度器正常执行完成。
   */
  public void complete() {
    if (!running)
      return;
    running = false;

    if (progressPanel != null)
      progressPanel.hide();

    if (listener != null)
      listener.onComplete(this);
  }

  /**
   * 通知调度器处理被用户中断。
   */
  public void interrupt() {
    if (!running)
      return;
    running = false;

    if (progressPanel != null)
      progressPanel.hide();

    if (listener != null)
      listener.onInterrupt(this);
  }

  /**
   * 通知调用器处理被意外中断。
   * 
   * @param msg
   *          指定导致意外中断的原因。
   * @param t
   *          指定导致处理中断的异常。允许null。
   */
  public void abort(String msg, Throwable t) {
    if (!running)
      return;
    running = false;

    if (progressPanel != null)
      progressPanel.hide();

    if (listener != null)
      listener.onAbort(this, msg, t);
  }

  /**
   * 返回是否处于运行状态。
   * 
   * @return
   */
  public boolean isRunning() {
    return this.running;
  }

  /**
   * 重置状态为初识值。
   */
  public void reset() {
    records = null;
    recordIndex = -1;
    env.clear();
    running = false;
    if (progressPanel != null)
      progressPanel.initial();
  }

  /**
   * 取得当前数据记录。
   * 
   * @return
   */
  public Object getCurrentRecord() {
    return records[recordIndex];
  }

  private boolean isInterrupted() {
    return progressPanel != null && progressPanel.isInterruptable()
        && progressPanel.isInterrupted();
  }

  private void dispatch() {
    Timer t = new Timer() {
      public void run() {
        call();
      }
    };
    t.schedule(1);
  }

  private void call() {
    Object record = null;
    if (records != null)
      record = records[recordIndex];
    if (listener != null)
      listener.onDispatche(this, record, recordIndex);
  }

}
